/*
 * ============================================================================
 *
 *       Filename:  main.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  02/01/2021 06:25:23 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  jianxi sun (jianxi), ycsunjane@gmail.com
 *   Organization:  
 *
 * ============================================================================
 */
#include <stdio.h>
#include <stdint.h>
#include <signal.h>
#include <unistd.h>
#include <sys/un.h>
#include <stdlib.h>
#include <time.h>
#include <ev.h>
#include <fcntl.h>

#include "main.h"

int debug = 0;
int daemon_enable = 0;
struct  gengetopt_args_info args_info;

static int file_exist(char *filename)
{
	/*  try to open file to read */
	FILE *file;
	if ((file = fopen(filename, "r"))){
		fclose(file);
		return 1;
	}
	return 0;
}

int setnonblocking(int fd)
{
	int flags;
	if (-1 == (flags = fcntl(fd, F_GETFL, 0))) {
		flags = 0;                                                      
	}

	return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

void proc_args(int argc, char *argv[])
{
	int ret;
	struct cmdline_parser_params *params;
	params = cmdline_parser_params_create();

	ret = cmdline_parser(argc, argv, &args_info);
	if(ret != 0) exit(ret);

	params->initialize = 0;
	params->override = 0;
	if(args_info.config_arg && 
		file_exist(args_info.config_arg)) {
		ret = cmdline_parser_config_file(
			args_info.config_arg,
			&args_info, params);
		if(ret != 0) exit(ret);
	}

	debug = args_info.debug_arg;
	daemon_enable = args_info.daemon_flag;
	free(params);
}

int create_and_bind(const char *addr, const char *port)
{
	struct addrinfo hints;
	struct addrinfo *result, *rp;
	int s, listen_sock = -1;

	memset(&hints, 0, sizeof(struct addrinfo));
	/* Return IPv4 and IPv6 choices */
	hints.ai_family   = AF_UNSPEC;
	/* We want a TCP socket */
	hints.ai_socktype = SOCK_STREAM;
	/* for listen when addr is NULL */
	hints.ai_flags = AI_PASSIVE;

	s = getaddrinfo(addr, port, &hints, &result);
	if (s != 0) {
		sys_err("getaddrinfo: %s", gai_strerror(s));
		return -1;
	}

	for (rp = result; rp != NULL; rp = rp->ai_next) {
		listen_sock = socket(rp->ai_family, 
			rp->ai_socktype, rp->ai_protocol);
		if (listen_sock == -1)
			continue;

		int opt = 1;
		setsockopt(listen_sock, SOL_SOCKET, 
			SO_REUSEADDR, &opt, sizeof(opt));
#ifdef SO_NOSIGPIPE
		setsockopt(listen_sock, SOL_SOCKET, 
			SO_NOSIGPIPE, &opt, sizeof(opt));
#endif
		s = bind(listen_sock, rp->ai_addr, rp->ai_addrlen);
		if (s == 0) {
			/* We managed to bind successfully! */
			break;
		} else {
			sys_err("Bind failed:%s(%d)\n", strerror(errno), errno);
		}

		close(listen_sock);
	}

	if(result)
		freeaddrinfo(result);

	if (rp == NULL) {
		sys_err("bind failed\n");
		return -1;
	}

	return listen_sock;
}

void close_and_free_cli(struct client_t *cli)
{
	ev_io_stop(cli->loop, & cli->recv_ctx->io);
	close(cli->fd);
	ss_free(cli->recv_ctx);
	bfree(cli->rxbuf);
}

char *s_popen(char *data, char *para)
{
	
	FILE *fp = NULL;
	if(para)
		fp = popen("/usr/sbin/get_module_msg.sh module", "r");
	else
		fp = popen("/usr/sbin/get_module_msg.sh gps", "r");

	if ( !fp ) {
		sys_err("popen /usr/sbin/get_module_msg.sh failed\n");
		return NULL;
	}

	if (fread(&data[0], 1, MSS, fp) > 0) {
		sys_debug("fread from get_module, data %s\n", data);
		fclose(fp);
		return data;
	}                                                                       

	sys_debug("fread from get_module failed \n");
	fclose(fp);
	return NULL;
}

static void cli_recv_cb(EV_P_ ev_io *w, int revents)
{
	sys_debug("cli recv cb activing ...\n");
	struct cli_ctx *cli_recv_ctx = container_of(w, struct cli_ctx, io);
	struct client_t *cli = cli_recv_ctx->cli;

	ssize_t r;
	char buf[128] = {0};
	r = recv(cli->fd, buf, 128, 0);
	sys_debug("fd: %u, %s, size: %lu\n", cli->fd, buf, r);
	if (r == 0) {
		/* client connection closed */
		close_and_free_cli(cli);
		return;
	} else if (r == -1) {
		if (errno == EAGAIN || errno == EWOULDBLOCK) {
			return;
		} else {
			sys_err("recv failed: %s(%d)\n",
				strerror(errno), errno);
			close_and_free_cli(cli);
			return;
		}
	} else {
		char data[MSS] = {0};
		if (strstr(buf, "ifname") || strstr(buf, "lte")) {
			if (s_popen(data, "module"))
				write(cli->fd, data, strlen(data));

		} else if (strstr(buf, "gnss") || strstr(buf, "gps")) {
			if (s_popen(data, NULL))
				write(cli->fd, data, strlen(data));
		}

		close_and_free_cli(cli);
	}
}

struct client_t *new_client(EV_P_ int fd)
{
	struct client_t *cli = NULL;
	cli = ss_malloc(sizeof(struct client_t));

	cli->loop     = EV_A;
	cli->fd       = fd;
	cli->recv_ctx = ss_malloc(sizeof(struct cli_ctx));
	cli->recv_ctx->cli = cli;

	ev_io_init(&cli->recv_ctx->io, cli_recv_cb, fd, EV_READ);

	return cli;
}

void accept_cb(EV_P_ ev_io *w, int revents)
{
	struct server_listen *listener = container_of(w, struct server_listen, io);
	int clifd;
	struct  sockaddr_in cliaddr;
	int socklen = sizeof(struct sockaddr_in);
	clifd = accept(listener->listenfd, (struct sockaddr *)&cliaddr, &socklen);
	if (clifd == -1) {
		sys_err("accept failed: %s(%d)\n", strerror(errno), errno);
		return;
	}
	setnonblocking(clifd);

	struct client_t *cli = new_client(listener->loop, clifd);
	memcpy(&cli->cliaddr, &cliaddr, socklen);

	/*  start recv socks5 */
	ev_io_start(listener->loop, &cli->recv_ctx->io);
}

static void signal_init()
{
	struct sigaction act;
	memset(&act, 0, sizeof(act));
	act.sa_handler = SIG_IGN;
	sigaction(SIGPIPE, &act, NULL);
	sigaction(SIGALRM, &act, NULL);
}

int main(int argc, char *argv[])
{

	proc_args(argc, argv);

	signal_init();

	if(args_info.daemon_flag)
		daemon(0, 0);

	struct ev_loop *loop = EV_DEFAULT;

	int listenfd = create_and_bind(NULL, args_info.local_port_arg);
	if (listenfd == -1) {
		sys_err("bind error\n");
		exit(1);
	}

	if (listen(listenfd, SOMAXCONN) == -1) {
		sys_err("listen failed: %s(%d)\n", strerror(errno), errno);
		exit(1);
	}
	setnonblocking(listenfd);

	struct server_listen slisten;
	bzero(&slisten, sizeof(slisten));

	slisten.loop = EV_DEFAULT;
	slisten.listenfd = listenfd;
	ev_io_init(&slisten.io, accept_cb, listenfd, EV_READ);

	serial_t *serial = create_serial();

	ev_io_start(loop, &slisten.io);
	ev_io_start(loop, &serial->recv_ctx->io);

	sys_debug("tcpserver: listen local port %s\n", args_info.local_port_arg);

	ev_run(loop, 0);


	return 0;
}
